package Task28;

import java.io.File;
import java.util.ArrayList;
import org.apache.log4j.Logger;

public class Controller {
	private static final Logger log = Logger.getLogger(Controller.class);
	private static final Logger history = Logger.getLogger("history");
	private ArrayList<String> errors = new ArrayList<String>();
	private ArrayList<String> checkedErrors = new ArrayList<String>();
	public static final String pathExist = "Path exist.";
	public static final String pathNotExist = "Path not exist.";
	private final String pathIsFile = "Directory is file.";
	private MainWindow mainWindow;

	public void setLinkToMainWindow(MainWindow myMainWindow) {
		mainWindow = myMainWindow;
	}

	private CheckErrorsInterface getInstance(String command) {
		CheckErrorsFactory checkErrorsFactory = new CheckErrorsFactory();
		CheckErrorsInterface instance = null;
		try {
			checkErrorsFactory.createMap();
			if (checkErrorsFactory.containsId(command))
				instance = checkErrorsFactory.getCheckErrorsInstance(command);
			else
				errors.add("Unknown name of command.");
		} catch (Exception e) {
			errors.add(e.getMessage());
		}
		return instance;
	}

	public Result getResult(Command command) {
		logInputCommand(command);
		historyInputCommand(command);

		Result result = new Result();
		result = getResultExceptionsAndCreateErrors(command);

		command = checkRenameCommand(command);
		result.addErrors(checkedErrors);

		if (complexSituationHappened())
			result.addResult(resultComplexSituation(command));
		else {
			if (!result.containProblems() && result.getMessage().isEmpty()) {
				if (command.getNameCommand().equalsIgnoreCase("copy")
						|| command.getNameCommand().equalsIgnoreCase("move")) {
					result.addResult(getResultInSeparateThread(command));
				} else {
					result.addResult(getResultFromModel(command));
				}
				if (command.getNameCommand().equalsIgnoreCase("xml"))
					result.addMessage("xml");
			}
		}
		logResult(result);
		return result;
	}

	private Result getResultFromModel(Command command) {
		Model myModel = new Model();
		Result resultModel = new Result();
		try {
			resultModel = myModel.getResult(command); // !!!!!!!!!!!
		} catch (Exception e) {
			resultModel.addExceptions(e.getMessage());
		}
		return resultModel;
	}

	private Result getResultInSeparateThread(Command command) {
		SeparateThread myThread = new SeparateThread(command, mainWindow);
		myThread.start();

		Result result = new Result();
		result.addData("Operation compleate.");
		return result;
	}

	private Result resultComplexSituation(Command command) {
		Result result = new Result();
		if (checkedErrors.contains(pathExist)) {
			result.addMessage(pathExist);
			checkedErrors.clear();
		}
		if (checkedErrors.contains(pathNotExist)) {
			result.addMessage(pathNotExist);
			checkedErrors.clear();
		}
		result.addData(command.getArgs()[0]);
		result.addData(command.getArgs()[1]);
		result.addData(command.getNameCommand());
		return result;
	}

	private Result getResultExceptionsAndCreateErrors(Command command) {
		Result result = new Result();
		String id = command.getNameCommand();
		CheckErrorsInterface instance = getInstance(id);
		result.addExceptions(errors);// add errors creating instance
		if (errors.isEmpty()) {
			checkedErrors = instance.getErrors(command);
		}
		return result;
	}

	private Command checkRenameCommand(Command command) {
		// rename command open XML
		if (command.getNameCommand().equalsIgnoreCase("cd")
				&& checkedErrors.contains(pathIsFile)) {
			command = renameCommandCd(command);
			checkedErrors.clear();
		}
		return command;
	}

	public class Copy extends CheckErrorsInterface {
		public static final String id = "copy";

		public Copy() {
		}

		@Override
		public ArrayList<String> getErrors(Command command) {
			ArrayList<String> result = new ArrayList<String>();
			result = nullChecks(command);

			if (command.getNumArgs() != 2) {
				result.add("Bad format of command");
			} else {
				String[] arg = new String[2];
				arg[0] = getAbsPath(command.getWorkDir(), command.getArgs()[0]);
				arg[1] = getAbsPath(command.getWorkDir(), command.getArgs()[1]);
				command.setArgs(arg);
			}
			if (!pathCheck(command.getWorkDir())) {
				result.add("Directory not found");
			}
			if (result.isEmpty()) {
				if (!pathCheck(command.getArgs()[0])) {
					result.add("File not found");
				}

				File file = new File(command.getArgs()[0]);
				File fileWorkDir = new File(command.getWorkDir());
				if (file.getPath().equals(fileWorkDir.getPath()))
					result.add("Directory is work dir. Can't copy it.");
				if (!file.exists())
					result.add("Directory not exist. " + file.getPath());

				file = new File(command.getArgs()[1]);
				if (file.getPath().equals(fileWorkDir.getPath()))
					result.add("Directory is work dir.");
				if (file.exists()) {
					result.add(pathExist); // throw my exception
				} else {
					if (!file.getParentFile().exists())
						result.add(pathNotExist);
				}
			}
			return result;
		}
	}

	public class Move extends CheckErrorsInterface {
		public static final String id = "move";

		public Move() {
		}

		@Override
		public ArrayList<String> getErrors(Command command) {
			ArrayList<String> result = new ArrayList<String>();
			result = nullChecks(command);

			if (command.getNumArgs() != 2) {
				result.add("Bad format of command");
			} else {
				String[] arg = new String[2];
				arg[0] = getAbsPath(command.getWorkDir(), command.getArgs()[0]);
				arg[1] = getAbsPath(command.getWorkDir(), command.getArgs()[1]);
				command.setArgs(arg);
			}
			if (!pathCheck(command.getWorkDir())) {
				result.add("Directory not found");
			}
			if (result.isEmpty()) {
				if (!pathCheck(command.getArgs()[0])) {
					result.add("File not found");
				}

				File file = new File(command.getArgs()[0]);
				File fileWorkDir = new File(command.getWorkDir());
				if (file.getPath().equals(fileWorkDir.getPath()))
					result.add("Directory is work dir. Can't copy it.");
				if (!file.exists())
					result.add("Directory not exist. " + file.getPath());

				file = new File(command.getArgs()[1]);
				if (file.getPath().equals(fileWorkDir.getPath()))
					result.add("Directory is work dir.");
				if (file.exists()) {
					result.add(pathExist); // throw my exception
				} else {
					if (!file.getParentFile().exists())
						result.add(pathNotExist);
				}
			}
			return result;
		}
	}

	public class Delete extends CheckErrorsInterface {
		public static final String id = "delete";

		public Delete() {
		}

		@Override
		public ArrayList<String> getErrors(Command command) {
			ArrayList<String> result = new ArrayList<String>();
			result = nullChecks(command);

			if (command.getNumArgs() != 1) {
				result.add("Bad format of command");
			} else {
				String[] arg = new String[1];
				arg[0] = getAbsPath(command.getWorkDir(), command.getArgs()[0]);
				command.setArgs(arg);
			}
			if (result.isEmpty()) {
				if (!pathCheck(command.getArgs()[0])) {
					result.add("File not found");
				} else {
					if (result.isEmpty()) {
						File file = new File(command.getArgs()[0]);
						File fileWorkDir = new File(command.getWorkDir());
						if (file.getPath().equals(fileWorkDir.getPath()))
							result.add("Directory is work dir. Can't delete it.");
						if (!file.exists())
							result.add("Directory not exist. " + file.getPath());
					}
				}
			}
			if (!pathCheck(command.getWorkDir())) {
				result.add("Directory not found");
			}
			return result;
		}

	}

	public class Dir extends CheckErrorsInterface {
		public static final String id = "dir";

		public Dir() {
		}

		@Override
		public ArrayList<String> getErrors(Command command) {
			ArrayList<String> result = new ArrayList<String>();
			result = nullChecks(command);

			if (command.getNumArgs() > 1) {
				result.add("Bad format of command");
			}
			if (command.getNumArgs() == 1) {
				String[] arg = new String[1];
				arg[0] = getAbsPath(command.getWorkDir(), command.getArgs()[0]);
				command.setArgs(arg);
			}
			if (command.getNumArgs() == 0) {
				String arg[] = new String[1];
				arg[0] = command.getWorkDir();
				command.setArgs(arg);
			}
			if (!pathCheck(command.getArgs()[0])) {
				result.add("Directory not found");
			} else {
				File file = new File(command.getArgs()[0]);
				if (file.isFile())
					result.add("Directory is file.");
			}
			if (!pathCheck(command.getWorkDir())) {
				result.add("Directory not found");
			}
			return result;
		}
	}

	public class Cd extends CheckErrorsInterface {
		public static final String id = "cd";

		public Cd() {
		}

		@Override
		public ArrayList<String> getErrors(Command command) {
			ArrayList<String> result = new ArrayList<String>();
			result = nullChecks(command);

			if (command.getNumArgs() > 1) {
				result.add("Bad format of command");
			}
			if (!pathCheck(command.getWorkDir())) {
				result.add("Directory not found");
			}
			if (result.isEmpty() && command.getNumArgs() > 0) {
				String[] arg = new String[1];
				arg[0] = getAbsPath(command.getWorkDir(), command.getArgs()[0]);
				command.setArgs(arg);

				if (!pathCheck(command.getArgs()[0])) {
					result.add("File not found");
				}

				File file = new File(command.getArgs()[0]);
				if (!file.exists())
					result.add("Directory not exist. " + file.getPath());

				if (file.isFile()) {
					result.add(pathIsFile);
				}
			}
			return result;
		}
	}

	public class MkDir extends CheckErrorsInterface {
		public static final String id = "mkdir";

		public MkDir() {
		}

		@Override
		public ArrayList<String> getErrors(Command command) {
			ArrayList<String> result = new ArrayList<String>();
			result = nullChecks(command);

			if (command.getNumArgs() != 1) {
				result.add("Bad format of command");
			} else {
				String[] arg = new String[1];
				arg[0] = getAbsPath(command.getWorkDir(), command.getArgs()[0]);
				command.setArgs(arg);
			}
			if (!pathCheck(command.getWorkDir())) {
				result.add("Directory not found");
			} else {
				if (result.isEmpty()) {
					if (command.getArgs()[0].contains("."))
						result.add("Directory is file.");
					if (command.getArgs()[0].equals(command.getWorkDir()))
						result.add("Directory is work dir. Can't create it.");
					File file = new File(command.getArgs()[0]);
					if (file.isFile())
						result.add("Directory is file. " + file.getPath());
					if (file.exists()) {
						result.add("Directory exist. " + file.getPath());
						// throw myexception
					}
				}
			}
			return result;
		}
	}

	public class Tree extends CheckErrorsInterface {
		public static final String id = "tree";

		public Tree() {
		}

		@Override
		public ArrayList<String> getErrors(Command command) {
			ArrayList<String> result = new ArrayList<String>();
			result = nullChecks(command);

			if (result.isEmpty()) {
				if (command.getNumArgs() > 1) {
					result.add("Bad format of command");
				}
				if (command.getNumArgs() == 1) {
					String[] arg = new String[1];
					arg[0] = getAbsPath(command.getWorkDir(),
							command.getArgs()[0]);
					command.setArgs(arg);
				}
				if (command.getNumArgs() == 0) {
					String arg[] = new String[1];
					arg[0] = command.getWorkDir();
					command.setArgs(arg);
				}
				if (!pathCheck(command.getArgs()[0])) {
					result.add("Directory not found");
				}
				File file = new File(command.getArgs()[0]);
				if (file.isFile())
					result.add("Directory is file.");
			}
			if (!pathCheck(command.getWorkDir())) {
				result.add("Directory not found");
			}
			return result;
		}

	}

	public class Find extends CheckErrorsInterface {
		public static final String id = "find";

		public Find() {
		}

		@Override
		public ArrayList<String> getErrors(Command command) {
			ArrayList<String> result = new ArrayList<String>();
			result = nullChecks(command);

			if (result.isEmpty()) {
				if (!(command.getNumArgs() == 2 || command.getNumArgs() == 1)) {
					result.add("Bad format of command");
				} else {
					if (command.getNumArgs() == 2) {
						String[] arg = new String[2];
						arg[0] = getAbsPath(command.getWorkDir(),
								command.getArgs()[0]);
						arg[1] = command.getArgs()[1];
						command.setArgs(arg);
					}
					if (command.getNumArgs() == 1) {
						String[] arg = new String[2];
						arg[0] = command.getWorkDir();
						arg[1] = command.getArgs()[0];
						command.setArgs(arg);
					}
					if (!pathCheck(command.getArgs()[0])) {
						result.add("Directory not found");
					}
					File file = new File(command.getArgs()[0]);
					if (file.isFile())
						result.add("Directory is file.");
				}
			}
			if (!pathCheck(command.getWorkDir())) {
				result.add("Directory not found");
			}
			return result;
		}
	}

	public class Exit extends CheckErrorsInterface {
		public static final String id = "exit";

		public Exit() {
		}

		@Override
		public ArrayList<String> getErrors(Command command) {
			ArrayList<String> result = new ArrayList<String>();
			return result;
		}

	}

	private boolean pathCheck(String directory) {
		File f = new File(directory);
		return f.exists();
	}

	public String getAbsPath(String directory, String arg) {// make
															// private!!!!!!!!
		// translate name of file and work directory in absolute directory
		File fArg = new File(arg);
		String absDir = "";
		if (fArg.exists() && !fArg.getPath().contains("..")) {
			absDir = arg;
		} else {
			File[] arrayRoots = File.listRoots();
			for (int i = 0; i < arrayRoots.length; i++) {
				String root = arrayRoots[i].getPath();
				if (arg.startsWith(root))
					absDir = arg;
			}
			if (absDir.equals(""))
				absDir = directory + File.separator + arg;
		}
		return absDir;
	}

	private ArrayList<String> nullChecks(Command command) {
		ArrayList<String> result = new ArrayList<String>();
		if (command.getWorkDir() == null) {
			result.add("Null in work direcory");
		}
		if (command.getArgs() == null) {
			result.add("Null in args");
		} else {
			if (command.getNumArgs() > 0) {
				if (command.getArgs()[0] == null) {
					result.add("Null in arg1");
				}
				if (command.getNumArgs() > 1) {
					if (command.getArgs()[1] == null) {
						result.add("Null in arg2");
					}
				}
			}
		}
		return result;
	}

	private boolean complexSituationHappened() {
		return checkedErrors.contains(pathExist)
				|| checkedErrors.contains(pathNotExist);

	}

	private void logInputCommand(Command command) {
		String message = "[Input command:] " + command.getNameCommand() + " ";
		for (int i = 0; i < command.getNumArgs(); i++) {
			message += command.getArgs()[i] + " ";
		}
		message += "[Work dir:] " + command.getWorkDir();
		log.info(message);
	}

	private void logResult(Result result) {
		if (!result.getMessage().isEmpty())
			log.error("[Output result:] "
					+ converterForLog(result.getMessage()));
		if (!result.getErrors().isEmpty())
			log.error("[Output result:] " + converterForLog(result.getErrors()));
		if (!result.getExceptions().isEmpty())
			log.error("[Output result:] "
					+ converterForLog(result.getExceptions()));
		if (result.getErrors().isEmpty() && result.getExceptions().isEmpty()
				&& result.getMessage().isEmpty())
			log.info("[Output result:] " + "Operation compleate.");
	}

	private String converterForLog(ArrayList<String> aString) {
		String result = "";
		for (int i = 0; i < aString.size(); i++) {
			result += aString.get(i) + " ";
		}
		return result;
	}

	private void historyInputCommand(Command command) {
		String message = command.getNameCommand();
		for (int i = 0; i < command.getNumArgs(); i++) {
			message += " " + command.getArgs()[i];
		}
		history.info(message);
	}

	private Command renameCommandCd(Command command) {
		File file = new File(command.getArgs()[0]);
		if (file.isFile()) {
			if (file.getName().endsWith("xml")
					|| file.getName().endsWith("XML"))
				command.setNameCommand("xml");
			else
				command.setNameCommand("open");
		}
		return command;
	}
}
